﻿using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using VIA.Integration.Automatics;
using VIA.Presentation;
using VIA.Project.Domain.Services;

namespace VIA.Project.Domain.Command
{
    public interface IPLCCommandServer : ICommandPanel
    {
        IPLC PLC { get; set; }
        List<DataTrigger> Triggers { get; set; }
        void Start(CancellationToken cancelToken);
    }

    public class PLCCommandServer : IPLCCommandServer
    {
        #region Filed
        protected ILogger _logger;
        protected IApplicationLog _applicationLog;
        private List<DataTrigger> _triggers;
        #endregion

        public IPLC PLC { get; set; }
        public List<DataTrigger> Triggers { get => _triggers; set => _triggers = value; }

        public event EventHandler<CommandReceivedEventArgs> CommandReceived;
        public PLCCommandServer(IServiceProvider service)
        {
            _logger = service.GetRequiredService<ILogger<PLCCommandServer>>();
            _applicationLog = service.GetRequiredService<IApplicationLog>();
            Triggers = new();
        }

        public void Start(CancellationToken cancelToken)
        {
            ushort ct = (ushort)Triggers.Count;
            if (ct == 0) return;
            OrderTriggers();
            ResetPCToPLCSignal();
            string address = Triggers[0].Address;
            var triggers = Triggers;
            Task.Factory.StartNew(() =>
            {
                while (PLC != null && PLC.IsConnected && !cancelToken.IsCancellationRequested)
                {
                    try
                    {
                        short[] shorts = PLC.ReadShort(address, ct);
                        for (int i = 0; i < shorts.Length; i++)
                        {
                            triggers[i].Value = shorts[i];
                        }
                        Thread.Sleep(30);
                    }
                    catch (Exception ex)
                    {
                        LogError(ex);
                    }
                }
            }, TaskCreationOptions.LongRunning);
        }

        private void OrderTriggers()
        {
            Triggers = Triggers.OrderBy(x => x.Address).ToList();
            foreach (var item in Triggers)
            {
                item.RisingEdge -= Trigger_RisingEdge;
                item.FallingEdge -= Trigger_FallingEdge;
                item.RisingEdge += Trigger_RisingEdge;
                item.FallingEdge += Trigger_FallingEdge;
            }
        }

        private void Trigger_RisingEdge(object? sender, string e)
        {
            DataTrigger trigger = sender as DataTrigger;
            LogInfo($"[{trigger.Address}]上升沿触发");
            if (trigger.IsRisingEdgeAutoFeedBack)
            {
                PLC.Write(trigger.FeedBackAddress, (ushort)1);
                LogInfo($"反馈[{trigger.FeedBackAddress}]信号:1");
            }
            OnCommandReceived(new CommandReceivedEventArgs(trigger.Address, trigger.Address));
        }

        private void Trigger_FallingEdge(object? sender, string e)
        {
            DataTrigger trigger = sender as DataTrigger;
            LogInfo($"[{trigger.Address}]下降沿触发");
            if (trigger.IsFallingEdgeAutoFeedBack)
            {
                PLC.Write(trigger.FeedBackAddress, (ushort)0);
                LogInfo($"[{trigger.FeedBackAddress}]清空信号:0");
            }
            OnCommandReceived(new CommandReceivedEventArgs(trigger.FeedBackAddress, trigger.FeedBackAddress));
        }

        private void OnCommandReceived(CommandReceivedEventArgs args)
        {
            CommandReceived?.Invoke(this, args);
        }

        public void ResetPCToPLCSignal()
        {
            if (PLC != null && PLC.IsConnected && Triggers.Count > 0)
            {
                int[] shorts = new int[Triggers.Count];
                string address = Triggers[0].FeedBackAddress;
                PLC.Write(address, shorts);
            }
        }

        protected void LogInfo(string message)
        {
            _logger.LogInformation(message);
            _applicationLog.LogInfo(LoggerCategory.Communication, message);
        }

        protected void LogError(Exception ex)
        {
            _logger.LogError(ex, ex.Message);
            _applicationLog.LogError(LoggerCategory.Communication, ex.Message);
        }

        protected void LogError(string message, Exception? ex)
        {
            _logger.LogError(ex, message);
            _applicationLog.LogError(LoggerCategory.Communication, message);
        }
    }

}
